<template>
  <div class="graphVis">
    <drawerPanel ref="child">
      <template v-slot:detail class="detail">
        <edit-node v-show="isShowNode" :isShowNode="isShowNode" :ontologyAllId="ontologyAllId"
          @changeByNode="changeByNode" @deleteNode="deleteNodeFun"></edit-node>
        <edit-edge v-show="!isShowNode" :isShowNode="isShowNode" :ontologyAllId="ontologyAllId"
          @changeByEdge="changeByEdge" ref="editEdgeForm" :graph="graph" @deleteEdge="deleteEdgeFun">
        </edit-edge>
      </template>
      <template v-slot:edit>
        <graph-config @changeGraphConfig="changeGraphConfig" @saveConfig="saveConfig" ref="graphConfig"
          @changeNodeTextSize="changeNodeTextSize" @changeEdgeTextSize="changeEdgeTextSize"
          @changePropertyColor="changePropertyColor" @changePropertyStyle="changePropertyStyle"></graph-config>
      </template>
    </drawerPanel>
    <tabs class="layoutComp" :class="[flag ? '' : 'hideRight']"></tabs>
    <div class="sliderComp" :class="[flag ? '' : 'hideRight']">
      <el-slider v-model="zoomVal" @input="outputZoom" :show-tooltip="false" :step="0.1" :min="0.2" :max="9" vertical
        height="200px">
      </el-slider>
    </div>

    <div class="addWrapper" :class="[flag ? '' : 'hideRight']">
      <div class="addLeft">
        <div class="addNodeComp" @click="toggleDrawer('add')">
          <div>
            <icon-park type="add-one" theme="outline" size="16" fill="#000000" :strokeWidth="2" />
          </div>
          新增节点
        </div>
      </div>
      <!-- <div class="addRight">
        <div class="addNodeComp" @click="selectGroup">
          <div>
            <icon-park
              type="add-one"
              theme="outline"
              size="16"
              fill="#000000"
              :strokeWidth="2"
            />
          </div>
          添加组件
        </div>
      </div> -->
      <div class="addRight">
        <div class="addNodeComp" @click="setProperty">
          <div>
            <i class="el-icon-orange"></i>
            <!-- <icon-park
              type="add-one"
              theme="outline"
              size="16"
              fill="#000000"
              :strokeWidth="2"
            /> -->
          </div>
          {{ this.propertyExpend ? "收起属性节点" : "展开属性节点" }}
        </div>
      </div>
    </div>
    <div id="graphContainer" class="content"></div>
    <div id="subGraphContainer"></div>
    <el-dialog title="保存组件" :visible.sync="dialogFormVisible" @close="afterDialogClose" class="dialogForm">
      <el-form :model="elementData" :rules="rules" ref="dialogForm" label-position="right" label-width="120px">
        <el-form-item label="组件名称:" prop="ontologyElementName" class="dialog-inline">
          <el-input maxlength="50" show-word-limit v-model.trim="elementData.ontologyElementName"></el-input>
        </el-form-item>
        <el-form-item label="组件描述:" prop="ontologyElementDescribe" class="dialog-inline">
          <el-input type="textarea" v-model.trim="elementData.ontologyElementDescribe" :autosize="textSizeConfig"
            maxlength="50" show-word-limit>
          </el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="resetDialogForm">取 消</el-button>
        <el-button type="primary" @click="submitDialogForm" :loading="loading">确 定</el-button>
      </div>
    </el-dialog>
    <insert-element-dialog :ontologyAllId="ontologyAllId" :dialogTableVisible="dialogTableVisible"
      @resetDialogTable="resetDialogTable" @submitDialogTable="submitDialogTable"></insert-element-dialog>
    <!-- <img :src="ontologyElementImgUrl" class="subGraphImg" alt=""> -->
  </div>
</template>
<script>
import G6 from '@antv/g6'
import drawerPanel from '@/components/drawerPanel/index.vue'
import editNode from './components/editNode.vue'
import editEdge from './components/editEdge.vue'
import tabs from './tabs/index.vue'
import insertElementDialog from './components/insertElementDialog.vue'
import { deepClone } from '@/plugins/utils'
import { mapState, mapMutations } from 'vuex'
import { IconPark } from '@icon-park/vue/es/all'
import graphConfig from '@/components/drawerPanel/graphConfig.vue'
import { initBehavors } from '@/behavior/index.js'
export default {
  name: 'OntologyModelCreate',
  components: {
    drawerPanel,
    editNode,
    editEdge,
    tabs,
    IconPark,
    insertElementDialog,
    graphConfig
  },
  data() {
    return {
      propertyColor: '#F4D35E',
      propertyStyle: '15',
      propertyListX: [],
      propertyExpend: false,
      // graphDatas: {},
      graphLeft: '', // graphContainer 的 left
      graphTop: '', // graphContainer 的 top
      timer2: null, // mounted 周期中的定时器
      elementList: [], // 组件列表
      elementData: {
        ontologyElementName: '', // 组件名称
        ontologyElementDescribe: '' // 组件描述
      },
      textSizeConfig: {
        minRows: 4,
        maxRows: 10
      },
      ontologyElementImgUrl: '', // 缩略图base64
      subGraph: null, // 用来生成缩略图的graph实例
      selectedItems: {}, // 框选中的节点和边
      isSelectGroup: false, // 是否是框选状态
      dialogFormVisible: false, // 新增组件模态框可见
      dialogTableVisible: false, // 选择组件模态框可见
      rules: {
        ontologyElementName: [
          {
            required: true,
            message: '请输入组件名称',
            trigger: 'blur'
          }
        ],
        ontologyElementDescribe: [
          {
            required: true,
            message: '请输入组件描述',
            trigger: 'blur'
          }
        ]
      },
      loading: false,
      // ontologyAllId: null, // 本体ID
      zoomVal: 1,
      width: null,
      height: null,
      graph: null,
      isFrameSelection: true,
      // verticalList: [{
      //     icon: "back",
      //     content: "撤销",
      //     code: "undo",
      //   },
      //   {
      //     icon: "download",
      //     content: "下载",
      //     code: "download",
      //   },
      //   {
      //     icon: "selected",
      //     content: "框选",
      //     code: "frameSelection",
      //   },
      //   {
      //     icon: "erase",
      //     content: "清空",
      //     code: "empty",
      //   },
      // ],
      verticalList: [
        {
          title: '只能撤销画布上节点的新增和修改操作',
          icon: 'back',
          content: '撤销',
          code: 'undo',
          icon: require('@/assets/svg/cancel.svg')
        },
        {
          title: '下载为图片',
          icon: 'download',
          content: '下载',
          code: 'download',
          icon: require('@/assets/svg/download.svg')
        },
        {
          title: '按住shift同时鼠标框选',
          icon: 'selected',
          content: '框选',
          code: 'frameSelection',
          icon: require('@/assets/svg/frameSelection.svg')
        },
        {
          title: '清空当前画布内容',
          icon: 'erase',
          content: '清空',
          code: 'empty',
          icon: require('@/assets/svg/empty.svg')
        }
      ],

      isShowNode: true,
      createNewEdge: {
        start: null,
        end: null
      },
      defaultNodeRadius: 16,
      undoList: []
    }
  },
  created() {
    initBehavors()
    this.animateFun()
    this.change_drawer_type_of_node('empty')
    this.change_isVisual(false)
  },
  mounted() {
    // let elementResizeDetectorMaker = require("element-resize-detector");
    // let marker = elementResizeDetectorMaker();
    this.timer2 = setTimeout(() => {
      let elementResizeDetectorMaker = require('element-resize-detector')
      let marker = elementResizeDetectorMaker()
      this.getGraphData(this.ontologyAllId).then(() => {
        const container = document.getElementById('graphContainer')
        if (container) {
          const rect = container.getBoundingClientRect()
          this.graphLeft = rect.left
          this.graphTop = rect.top
          // 获取图谱容器的宽高
          this.getGraphContainerCon()
          if (this.width && this.height) {
            this.initGraph()
          }
          // 初始化图谱交互事件
          this.initGraphEvent()
          //画布随浏览器大小改变
          window.onresize = () => {
            if (!this.graph || this.graph.get('destroyed')) return
            this.getGraphContainerCon()
            this.graph.changeSize(this.width, this.height)
          }
          marker.listenTo(container, () => {
            this.getGraphContainerCon()
            this.graph.changeSize(this.width, this.height)
          })
          this.$eventBus.$on('refreshLayout', this.refreshGraphLayout)
          this.$eventBus.$on('toCenter', this.nodeToCenter)
          this.$eventBus.$on('refreshOntology', this.refreshOntology)
        }
      })
    }, 1000)
  },
  beforeRouteLeave(to, from, next) {
    this.change_drawer_type_of_node('empty')
    this.change_drawer_type_of_edge('empty')
    this.create_new_edge_of_g6({})
    this.change_current_node()
    this.change_current_edge()
    if (!this.flag) {
      this.change_drawerFlag()
    }

    next()
  },
  beforeDestroy() {
    this.$eventBus.$off('refreshLayout')
    this.$eventBus.$off('toCenter')
    this.$eventBus.$off('refreshOntology')
    this.timer2 = null
  },
  computed: {
    ...mapState({
      flag: state => state.ontologyModel.drawerFlag, // 右侧抽屉打开/关闭
      graphDatas: state => state.ontologyModel.graphDatas, // 图数据
      drawerTypeOfNode: state => state.ontologyModel.drawerTypeOfNode, // 抽屉详情节点状态 add,detail,edit
      drawerTypeOfEdge: state => state.ontologyModel.drawerTypeOfEdge, // 抽屉详情边状态 同上
      newEdgeOfG6: state => state.ontologyModel.newEdgeOfG6,
      node_config_data: state => state.ontologyModel.node_config_data, // 节点数据
      edge_config_data: state => state.ontologyModel.edge_config_data, // 边数据
      ontologyAllId: state => state.ontologyModel.ontologyAllId
    })
  },
  methods: {
    ...mapMutations({
      change_drawerFlag: 'ontologyModel/change_drawerFlag',
      save_nodesId: 'ontologyModel/save_nodesId',
      save_graphData: 'ontologyModel/save_graphData',
      change_drawer_type_of_node: 'ontologyModel/change_drawer_type_of_node',
      change_drawer_type_of_edge: 'ontologyModel/change_drawer_type_of_edge',
      create_new_edge_of_g6: 'ontologyModel/create_new_edge_of_g6',
      change_current_node: 'ontologyModel/change_current_node',
      change_current_edge: 'ontologyModel/change_current_edge',
      create_node_config_data: 'ontologyModel/create_node_config_data',
      create_edge_config_data: 'ontologyModel/create_edge_config_data',
      remove_node_config_data: 'ontologyModel/remove_node_config_data',
      remove_edge_config_data: 'ontologyModel/remove_edge_config_data',
      change_isVisual: 'ontologyModel/change_isVisual'
    }),
    // 设置节点文字大小
    changeNodeTextSize(val) {
      const nodes = this.graph.getNodes()
      if (nodes) {
        nodes.forEach(item => {
          this.graph.updateItem(item, {
            labelCfg: {
              style: {
                fontSize: val
              }
            }
          })
        })
      }
    },
    // 设置边文字大小
    changeEdgeTextSize(val) {
      const edges = this.graph.getEdges()
      if (edges) {
        edges.forEach(item => {
          this.graph.updateItem(item, {
            labelCfg: {
              style: {
                fontSize: val
              }
            }
          })
        })
      }
    },
    changePropertyColor(val) {
      this.propertyListX.forEach(ens => {
        if (ens.includes('prop')) {
          this.graph.updateItem(ens, { style: { fill: val } })
        }
      })
      this.propertyColor = val
    },
    changePropertyStyle(val) {
      this.propertyListX.forEach(ens => {
        if (ens.includes('prop')) {
          this.graph.updateItem(ens, { size: val * 2 })
        }
      })
      this.propertyStyle = val
    },
    refreshOntology(arg) {
      this.graph.destroy()
      this.getGraphData(arg).then(() => {
        let elementResizeDetectorMaker = require('element-resize-detector')
        let marker = elementResizeDetectorMaker()
        const container = document.getElementById('graphContainer')
        // 获取图谱容器的宽高
        this.getGraphContainerCon()
        if (this.width && this.height) {
          this.initGraph()
        }
        // 初始化图谱交互事件
        this.initGraphEvent()
        //画布随浏览器大小改变
        window.onresize = () => {
          if (!this.graph || this.graph.get('destroyed')) return
          this.getGraphContainerCon()
          this.graph.changeSize(this.width, this.height)
        }
        marker.listenTo(container, () => {
          this.getGraphContainerCon()
          this.graph.changeSize(this.width, this.height)
        })
        // this.$eventBus.$on('refreshLayout', this.refreshGraphLayout)
        // this.$eventBus.$on('toCenter', this.nodeToCenter)
        // this.$eventBus.$on('refreshOntology', this.refreshOntology)
      })
    },
    setProperty({ isInitSet, initData }) {
      if (this.propertyExpend) {
        if (this.propertyListX) {
          this.propertyListX.forEach(es => {
            this.graph.removeItem(es)
          })
        }
        this.propertyListX = []
        this.propertyExpend = !this.propertyExpend
      } else {
        let ontologyIds = this.graph.getNodes().map(ns => {
          return ns.getModel().id
        })
        this.$axios
          .post(this.$api.ontologyModel.xinbiaoObj.propertyShow, {
            ids: ontologyIds
          })
          .then(res => {
            let data = res.data.data
            if (data) {
              data.forEach(ds => {
                if (ds.propertyList.length > 0) {
                  let ontologyNode = this.graph.findById(ds.ontologyId).getModel()
                  const { x: oldX, y: oldY } = ontologyNode
                  let laynodes = []
                  let layedges = []
                  ds.propertyList.forEach((item, index) => {
                    //半径
                    var radius = 80
                    //角度

                    //角度计算

                    //角度值
                    var itemAngle = index * 15
                    //坐标
                    var xx = oldX + radius * Math.sin((itemAngle * 3.14) / 180)
                    var yx = oldY + radius * Math.cos((itemAngle * 3.14) / 180)
                    const newNode = {
                      id: ds.ontologyId + 'prop' + index,
                      label: item,
                      x: xx,
                      y: yx,
                      style: {
                        fill: this.propertyColor
                      },
                      size: this.propertyStyle * 2
                    }
                    const newEdge = {
                      id: ds.ontologyId + 'edge' + index,
                      source: ds.ontologyId,
                      target: ds.ontologyId + 'prop' + index
                    }
                    this.propertyListX.push(ds.ontologyId + 'prop' + index)
                    this.propertyListX.push(ds.ontologyId + 'edge' + index)
                    this.graph.addItem('node', newNode, false)
                    this.graph.addItem('edge', newEdge, false)

                    laynodes.push(
                      this.graph
                        .findById(ds.ontologyId + 'prop' + index)
                        .getModel()
                    )
                    layedges.push(
                      this.graph
                        .findById(ds.ontologyId + 'edge' + index)
                        .getModel()
                    )
                  })
                }
              })
            }
            this.propertyExpend = !this.propertyExpend
            if (isInitSet) {
              this.changeNodeTextSize(initData.textSize)
              this.changeEdgeTextSize(initData.sideSize)
              this.refreshGraphLayout(initData.graphLayoutWay)
              this.changeGraphConfig({
                nodeShape: "2",
                nodeLabel: initData.nodeLabel
              })
            }
          })
          .catch(err => {
            // this.$message.error(err)
          })
      }
    },

    changeByNode(data) {
      // 新增节点 （新增边和节点/只新增节点）add操作，编辑节点（只改变节点，改变节点和删除边，改变节点和新增边，改变节点和删除和新增边）search 节点并更新，
      let { flag, nodes, addEdge, delEdgeID } = data
      if (this.drawerTypeOfNode === 'add') {
        this.addNode(nodes)
        let { id } = nodes
        let singleNode = this.graph.findById(id)
        this.graph.setItemState(singleNode, 'click', false)
        // 新增节点之后重新请求一下所有数据, 拿到最新数据, 并将数据更新到面板上
        this.getGraphData(this.ontologyAllId)
          .then(res => {
            this.create_node_config_data(deepClone(res.ontologyNodes))
            this.create_edge_config_data(deepClone(res.ontologyEdges))
          })
          .catch()
        // add节点 push
      } else {
        this.updateNode(nodes)
        // 查找节点并更新 find setItem
      }
      if (flag) {
        if (Array.isArray(addEdge)) {
          addEdge.forEach(item => {
            this.addEdge(item)
          })
        } else {
          if (addEdge && addEdge.id) {
            this.addEdge(addEdge)
            // 新增的边push add
          }
          if (delEdgeID) {
            this.removeEdge({
              id: delEdgeID
            })
            // 删除的边 splice remove
          }
        }
      }
      // 存储operationId
      this.undoList.push({
        type: data.operationType,
        operationId: data.operationId
      })
    },
    // 监听边新增/更新
    changeByEdge(edgeData, type) {
      if (type === 'add') {
        let { g6Id, edge } = edgeData
        this.removeEdge({
          id: g6Id
        })
        this.addEdge(edge)
      } else {
        let { edge } = edgeData
        this.updateEdge(edge.id, edge)
      }
      // 新增节点之后重新请求一下所有数据, 拿到最新数据, 并将数据更新到面板上
      this.getGraphData(this.ontologyAllId)
        .then(res => {
          this.create_edge_config_data(deepClone(res.ontologyEdges))
        })
        .catch()
    },
    // 获取图谱容器的宽高
    getGraphContainerCon() {
      const graphCon = document.getElementById('graphContainer')
      this.width = graphCon.offsetWidth
      this.height = graphCon.offsetHeight
    },
    // 缩放图谱
    outputZoom(val) {
      if (this.graph) {
        this.graph.zoomTo(val)
      }
    },
    // 获取图谱中所有节点的 id 组成的数组
    getAllNodeId() {
      let arrId = []
      this.graph.getNodes().forEach(item => {
        arrId.push(item.getModel().id)
      })
      // 将数据存到 store
      this.save_nodesId(arrId)
      return arrId
    },
    // 将目标节点移到图中心
    nodeToCenter(val) {
      const node = this.graph.findById(val)
      let model = {
        type: 'circle-animate'
      }
      // 添加动画
      this.graph.updateItem(node, model)
      // 整体移动图谱使得当前节点位于中心
      this.graph.focusItem(node, true, {
        easing: 'easeCubic',
        duration: 500
      })
      let timer = setTimeout(() => {
        model = {
          type: 'circle'
        }
        this.graph.updateItem(node, model)
      }, 3500)
      timer = null
    },
    // 新增节点
    toggleDrawer() {
      this.change_drawer_type_of_node('add')
      this.isShowNode = true
      // 先发开侧边栏, 添加节点信息, 之后保存, 使用返回的信息构建 model, 然后添加节点
      if (this.flag) {
        this.change_drawerFlag()
      }
      this.change_current_node({})
      this.$refs.child.handleClick('detail')
    },
    // 新增节点至图谱
    addNode(node) {
      // 将屏幕坐标转换为渲染坐标, 使得缩放、平移画布后新增的节点总是出现在画布的左上方
      const point = this.graph.getPointByClient(
        this.graphLeft + 50,
        this.graphTop + 50
      )
      let { id, ontologyId, label } = node
      const model = {
        x: point.x,
        y: point.y,
        size: this.defaultNodeRadius * 2,
        id,
        ontologyId,
        label,
        style: {
          fill: '#7E9BFA'
        }
      }
      this.graph.addItem('node', model, false)
    },
    // 更新节点信息
    updateNode(nodeData) {
      let { id, x, y, ontologyId, label } = nodeData
      let model = {
        id,
        ontologyId,
        label
      }
      if (x || y) {
        model.x = x
        model.y = y
      }
      const node = this.graph.findById(id)
      this.graph.updateItem(node, model)
    },
    deleteNodeFun(nodeData) {
      this.$confirm(
        `是否删除本体类“${nodeData.name ? nodeData.name : nodeData.label}”？<br>
        删除该节点后，该节点将被禁用，将不能再显示在画布中。`,
        '提示',
        {
          dangerouslyUseHTMLString: true,
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }
      )
        .then(async () => {
          if (!nodeData.ontologyId) {
            nodeData.ontologyId = this.graph
              .findById(nodeData.id)
              .getModel().ontologyId
          }
          let { ontologyId } = nodeData
          let res = await this.$axios.get(
            this.$api.ontologyModel.xuekuiObj.deleteNode,
            {
              params: {
                ontologyId
              }
            }
          )
          if (res.data) {
            if (res.data.code === 200) {
              this.removeNode(nodeData)
              this.$message.success('删除成功!')
            } else {
              this.$message.error(res.data.message)
            }
          }
        })
        .catch(() => {
          this.$message.info('已取消删除')
        })
    },
    // 删除节点
    removeNode(nodeData) {
      let { id } = nodeData
      const node = this.graph.findById(id)
      let edges = node.getEdges()
      edges.forEach(edge => {
        this.remove_edge_config_data(edge.getModel().id)
      })
      this.graph.removeItem(node)
      this.change_drawer_type_of_node('empty')
      this.remove_node_config_data(id)
    },
    // 新增边
    addEdge(edgeData) {
      let model = edgeData
      if (edgeData.source === edgeData.target) {
        model = {
          ...edgeData,
          type: 'loop'
        }
      }
      this.graph.addItem('edge', model)
      const edges = this.graph.save().edges
      G6.Util.processParallelEdges(edges)
    },
    // 更新边
    updateEdge(id, edgeData) {
      const edge = this.graph.findById(id)
      const source = edge.getModel().source
      const target = edge.getModel().target
      const sourceNodeSize = this.graph.findById(source).getModel().size
      const targetNodeSize = this.graph.findById(target).getModel().size

      const sourceNodeRadius = sourceNodeSize / 2 + 2
      const targetNodeRadius = targetNodeSize / 2 + 2

      let { label } = edgeData
      let arrow =
        edgeData.edgeType === 'two-way'
          ? {
            path: G6.Arrow.triangle(5, 10, sourceNodeRadius),
            d: sourceNodeRadius
          }
          : false
      let model = {
        label,
        style: {
          startArrow: arrow
        }
      }
      this.graph.updateItem(edge, model)
      this.graph.setItemState(edge, 'click', true)
    },
    deleteEdgeFun(edgeData) {
      const edge = this.graph.findById(edgeData.id)
      const label = edge.getModel().label
      const sourceLabel = edge.getSource().getModel().label
      const targetLabel = edge.getTarget().getModel().label
      this.$confirm(
        `是否删除本体关系“${sourceLabel}-${label}-${targetLabel}”？<br>
        删除该关系后，该关系将被禁用，将不能再显示在画布中。`,
        '提示',
        {
          dangerouslyUseHTMLString: true,
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }
      )
        .then(async () => {
          let { id } = edgeData
          let res = await this.$axios.get(
            this.$api.ontologyModel.xuekuiObj.deleteOntologySpo,
            {
              params: {
                neo4jId: id
              }
            }
          )
          if (res.data && res.data.code === 200) {
            this.removeEdge(edgeData)
            this.$message.success('删除成功!')
          } else {
            this.$message.error(res.data.message)
          }
        })
        .catch(() => {
          this.$message.info('已取消删除')
        })
    },
    // 删除边
    removeEdge(edgeData) {
      let { id } = edgeData
      const edge = this.graph.findById(id)
      this.graph.removeItem(edge)
      this.change_drawer_type_of_edge('empty')
    },
    // 重新布局
    refreshGraphLayout(val) {
      if (this.graph) {
        if (!val) {
          this.graph.updateLayout({
            type: 'force',
            linkDistance: 300,
            preventOverlap: true // 防止节点覆盖
          })
        } else {
          this.graph.updateLayout({
            type: val,
            linkDistance: 300,
            preventOverlap: true // 防止节点覆盖
          })
        }
      } else {
        this.$notify({
          title: '提示',
          message: '请先生成图谱！',
          type: 'warning',
          duration: 1500
        })
      }
    },
    // 切换全屏
    toggleFullScreen() {
      const graphContainer = document.getElementById('graphContainer')
      const canvas = graphContainer.querySelector('canvas')
      if (!canvas.fullscreenElement) {
        canvas.requestFullscreen()
      } else {
        if (canvas.exitFullscreen) {
          canvas.exitFullscreen()
        }
      }
    },
    // 清除所有样式
    clearAllStates() {
      this.graph.getNodes().forEach(item => {
        this.graph.clearItemStates(item)
      })
      this.graph.getEdges().forEach(item => {
        this.graph.clearItemStates(item)
      })
    },
    claerItemClicked() {
      const clickedEdges = this.graph.findAllByState('edge', 'click')
      clickedEdges.forEach(ce => {
        this.graph.setItemState(ce, 'click', false)
      })
      // 先将所有的 click 状态的节点置为非 click 状态
      const clickedNodes = this.graph.findAllByState('node', 'click')
      clickedNodes.forEach(cn => {
        this.graph.setItemState(cn, 'click', false)
      })
    },
    // 通过点击建模获取的 id 获取图谱数据
    getGraphData(ontologyAllId) {
      const params = {
        ontologyAllId
      }
      return this.$axios
        .get(this.$api.ontologyModel.xuekuiObj.getAllData, {
          params
        })
        .then(res => {
          let data = res.data.data
          data.ontologyNodes.forEach(item => {
            if (item.img) {
              // this.getImg(item.img).then((res) => {
              //   item.img = res;
              // });
            }
          })

          if (this.$refs.graphConfig) {
            // this.graphDatas = data
            this.$refs.graphConfig.propertyColor =
              data.propertyColor == '' ? '#F4D35E' : data.propertyColor
            this.$refs.graphConfig.propertyStyle =
              data.propertyStyle == '' ? '15' : data.propertyStyle
            this.propertyColor = this.$refs.graphConfig.propertyColor
            this.propertyStyle = this.$refs.graphConfig.propertyStyle
          }
          this.save_graphData(data)
          return data
        })
        .catch(err => {
          this.$message.error(err)
        })
    },

    async getImg(imgUrl) {
      const params = {
        imgUrl
      }
      let res = await this.$axios({
        methods: 'get',
        url: this.$api.ontologyModel.xuekuiObj.getImg,
        params,
        responseType: 'blob'
      })
      let blob = new Blob([res.data])
      let url = window.URL.createObjectURL(blob)
      return url
    },

    animateFun() {
      G6.registerNode(
        'circle-animate',
        {
          afterDraw(cfg, group) {
            // 获取该节点上的第一个图形
            const shape = group.get('children')[0]
            // 该图形的动画
            shape.animate(
              ratio => {
                // 每一帧的操作，入参 ratio：这一帧的比例值（Number）。返回值：这一帧需要变化的参数集（Object）。
                // 先变大、再变小
                const diff = ratio <= 0.5 ? ratio * 10 : (1 - ratio) * 10
                let radius = cfg.size
                if (isNaN(radius)) radius = radius[0]
                // 返回这一帧需要变化的参数集，这里只包含了半径
                return {
                  r: radius / 2 + diff
                }
              },
              {
                // 动画重复
                repeat: false,
                duration: 3000,
                easing: 'easeCubic'
              }
            ) // 一次动画持续的时长为 3000，动画效果为 'easeCubic'
          }
        },
        'circle'
      )
    },
    initGraph() {
      // 小地图
      const minimap = new G6.Minimap({
        size: [200, 100]
      })
      // 右键菜单
      const contextMenu = new G6.Menu({
        shouldBegin: evt => {
          if (
            evt.target &&
            evt.target.isCanvas &&
            evt.target.isCanvas() &&
            this.isSelectGroup
          )
            return true
          if (evt.item) return true
          return false
        },
        getContent: evt => {
          // if (evt.target && evt.target.isCanvas && evt.target.isCanvas()) {
          //   if (this.isSelectGroup) {
          //     return `<ul>
          //                                 <li id='save'>保存组件</li>
          //                             </ul>`
          //   }
          // }
          // const {
          //   item
          // } = evt
          // const itemType = item.getType()
          // const model = item.getModel()
          // if (itemType && model) {
          //   if (itemType === 'node') {
          //     return `<ul>
          //                                 <li  id='connect'>连线</li>
          //                                 <li id='extendnode'>展开</li>
          //                                 <li id='hidenode'>收起</li>
          //                                 <li id='hideme'>隐藏</li>
          //                                 <li id='delete'>删除</li>
          //                             </ul>`
          //   } else if (itemType === 'edge') {
          //     return `<ul>
          //                                 <li  id='delete'>删除</li>
          //                             </ul>`
          //   }
          // }
          if (this.isSelectGroup) {
            return `<ul>
                        <li id='save'>保存组件</li>
                        <li id='hideSelected'>隐藏</li>
                    </ul>`
          } else {
            const { item } = evt
            const itemType = item.getType()
            const model = item.getModel()
            if (itemType && model) {
              if (itemType === 'node') {
                return `<ul>
                            <li  id='connect'>连线</li>
                            <li id='extendnode'>展开</li>
                            <li id='hidenode'>收起</li>
                            <li id='hideme'>隐藏</li>
                            <li id='delete'>删除</li>
                        </ul>`
              } else if (itemType === 'edge') {
                return `<ul>
                            <li  id='delete'>删除</li>
                        </ul>`
              }
            }
          }
        },
        handleMenuClick: (target, item) => {
          const liIdStrs = target.id.split('-')
          switch (liIdStrs[0]) {
            case 'connect':
              this.clearAllStates()
              this.graph.setMode('create-edge')
              this.graph.emit('node:click', {
                item
              })
              // this.change_drawer_type_of_edge('add')
              break
            case 'extendnode':
              const g = this.graph.getNeighbors(item._cfg.id, 'target')
              for (let i = 0; i < g.length; i++) {
                this.graph.showItem(g[i]._cfg.id)
              }
              break
            case 'hidenode':
              const g1 = this.graph.getNeighbors(item._cfg.id, 'target')
              for (let i = 0; i < g1.length; i++) {
                this.graph.hideItem(g1[i]._cfg.id)
              }
              break
            case 'hideme':
              // this.graph.hideItem(item)
              this.graph.removeItem(item._cfg.id)
              const ddd = this.graph.save()
              const { nodes: ontologyNodes, edges: ontologyEdges } = ddd
              this.save_graphData({
                ontologyNodes,
                ontologyEdges
              })
              break
            case 'delete':
              if (item.getModel().type == 'circle') {
                // 删除节点
                this.deleteNodeFun(item.getModel())
              } else {
                // 删除边
                this.deleteEdgeFun(item.getModel())
              }

              break
            case 'save':
              this.initGraphForImage()
              this.dialogFormVisible = true
              break
            case 'hideSelected':
              const { nodes, edges } = this.selectedItems
              nodes.forEach(item => {
                let { id } = item
                let instance = this.graph.findById(id)
                this.graph.hideItem(instance)
              })
              edges.forEach(item => {
                let { id } = item
                let instance = this.graph.findById(id)
                this.graph.hideItem(instance)
              })
              break
            default:
              break
          }
        },
        itemTypes: ['node', 'edge', 'canvas']
      })
      // 工具栏
      const toolbar = new G6.ToolBar({
        className: 'toolbar',
        getContent: () => {
          let list = this.verticalList.map(item => {
            if (item.code != 'download') {
              return `
              <li code="${item.code}" class="toolbar-item">
                <div class="toolbar-main">
                  <img src="${item.icon}" style="width: 16px; height: 16px; margin-left: 4px;" />
                  <div>${item.content}</div>
                </div>
                <span class="toolbar-tips" style="left: -150px;top: -4px;">${item.title}</span>
              </li>`
            } else {
              return `
              <li class="toolbar-item">
                <div class="toolbar-main">
                  <img src="${item.icon}" style="width: 16px; height: 16px; margin-left: 4px;" />
                  <div>${item.content}</div>
                </div>
                <div class="toolbar-opera">
                  <span id="pngDownload" code="pngCode">png图片</span>
                  <span id="jpgDownload" code="jpgCode">jpg图片</span>
                  <span id="jpegDownload" code="jpegCode">jpeg图片</span>
                  <span id="svgDownload" code="svgCode">svg图片</span>
                </div>
              </li>`
            }
          })
          list = list.join('')
          return `<ul>${list}</ul>`
        },
        handleClick: (code, graph, target) => {
          switch (code) {
            case 'undo':
              // toolbar.undo();
              this.undo()
              break
            case 'download':
              if (graph && graph.save().nodes.length != 0) {
                graph.downloadFullImage('云测图谱', 'image/png', {
                  backgroundColor: '#ddd',
                  padding: [30, 15, 15, 15]
                })
              } else {
                this.$notify({
                  title: '提示',
                  message: '请先生成图谱！',
                  type: 'warning',
                  duration: 1500
                })
              }
              break
            case 'fullScreen':
              this.toggleFullScreen()
              break
            case 'frameSelection':
              // if (graph.getCurrentMode() != "box-select") {
              //   this.clearAllStates();
              //   graph.setMode("box-select");
              //   this.isFrameSelection = false;
              // } else {
              //   this.clearAllStates();
              //   graph.setMode("default");
              // }
              break
            case 'empty':
              this.$confirm(
                '是否确认清空当前画布上的全部本体类？清空后不可恢复',
                '提示',
                {
                  confirmButtonText: '确定',
                  cancelButtonText: '取消',
                  type: 'warning'
                }
              )
                .then(() => {
                  // 接口返回删除成功后, 再销毁画布
                  // if (!this.$store.state.hasGraph) {
                  if (true) {
                    // 清空数据
                    this.clearGraph()
                  }
                })
                .catch(() => {
                  this.$message({
                    type: 'info',
                    message: '已取消删除'
                  })
                })
            default:
              break
          }
        }
      })

      this.graph = new G6.Graph({
        container: 'graphContainer',
        width: this.width,
        height: this.height,
        renderer: 'canvas',
        plugins: [minimap, contextMenu, toolbar],
        animate: true,
        enabledStack: true,
        linkCenter: true,
        layout: {
          type: 'gForce',
          linkDistance: 300,
          preventOverlap: true // 防止节点覆盖
        },
        // 交互模式：包括节点和边的信息浮框, 缩放/拖拽画布、节点等
        modes: {
          default: [
            'drag-canvas',
            'drag-node',
            {
              type: 'zoomCanvas',
              minZoom: 0.2,
              maxZoom: 10
            },
            {
              type: 'brush-select',
              trigger: 'shift'
            }
          ],
          'box-select': ['brush-select', 'drag-node'],
          'create-edge': [
            'drag-canvas',
            'drag-node',
            // "zoom-canvas",
            {
              type: 'add-edge',
              edgeConfig: {
                label: '未命名',
                type: 'line',
                style: {
                  stroke: '#E63946'
                },
                labelCfg: {
                  autoRotate: true,
                  style: {
                    fill: '#247BA0',
                    //导致G6画线错误的地方：原因是 fontSize:"16px" 导致text文本渲染错误，catchBBox 重新渲染区域盒子获取数据有误
                    fontSize: 16,
                    fontWeight: 700
                  }
                }
              }
            }
          ]
        },
        defaultNode: {
          // size: 60,
          style: {
            fill: '#F4D35E', // 填充色
            stroke: '#BFC0C0', // 描边颜色
            lineWidth: 0, // 描边宽度
            cursor: 'pointer'
          },
          labelCfg: {
            position: 'bottom' //"center"为内嵌，"bottom"为置底
          },
          clipCfg: {
            show: false,
            type: 'circle',
            r: 30,
            // ellipse
            rx: 10,
            ry: 15,
            // rect
            width: 15,
            height: 15,
            // 坐标
            x: 0,
            y: 0
          }
        },
        defaultEdge: {
          type: 'line',
          style: {
            lineWidth: '2',
            stroke: '#A9B6C3',
            // lineAppendWidth: 10,
            // startArrow: true,
            endArrow: {
              path: G6.Arrow.triangle(5, 10, this.defaultNodeRadius + 2),
              d: this.defaultNodeRadius + 2
            }
          },
          labelCfg: {
            autoRotate: true
          }
        },
        nodeStateStyles: {
          hover: {
            fill: 'lightsteelblue'
          },
          click: {
            stroke: '#4C74F8',
            lineWidth: 2
          }
        },
        edgeStateStyles: {
          click: {
            stroke: '#FFB997'
          }
        }
      })
      setTimeout(() => {
        const pngDownload = document.getElementById('pngDownload')
        const jpgDownload = document.getElementById('jpgDownload')
        const jpegDownload = document.getElementById('jpegDownload')
        const svgDownload = document.getElementById('svgDownload')
        pngDownload.addEventListener('click', () => {
          this.downloadGraph('image/png')
        })
        jpgDownload.addEventListener('click', () => {
          this.downloadGraph('image/jpg')
        })
        jpegDownload.addEventListener('click', () => {
          this.downloadGraph('image/jpeg')
        })
        svgDownload.addEventListener('click', () => {
          this.downloadSvgGraph()
        })
      }, 1000)
      const { ontologyNodes, ontologyEdges } = this.graphDatas
      this.create_node_config_data(deepClone(ontologyNodes))
      this.create_edge_config_data(deepClone(ontologyEdges))

      ontologyNodes.forEach(node => {
        node.style = {
          fill: node.color
        }
      })

      ontologyEdges.forEach(edge => {
        if (edge['source'] == edge['target']) {
          edge.type = 'loop'
        }
        edge.style = {
          stroke: edge.color
        }
      })
      // for (let i = 0; i < ontologyEdges.length; i++) {
      //   const element = ontologyEdges[i]
      //   if (element['source'] == element['target']) {
      //     element.type = 'loop'
      //   }
      // }
      G6.Util.processParallelEdges(ontologyEdges)
      this.graph.getEdges().forEach((edge, i) => {
        this.graph.updateItem(edge, {
          curveOffset: edges[i].curveOffset,
          curvePosition: edges[i].curvePosition
        })
      })
      this.graph.data({
        nodes: ontologyNodes,
        edges: ontologyEdges
      }) // 读取远程数据到图上
      this.graph.render()
      this.graph.getEdges().forEach(edge => {
        let isHasArrow = edge.getModel().type === 'loop' ? false : true
        let defaultRadius = this.defaultNodeRadius + 2
        this.graph.updateItem(edge, {
          style: {
            startArrow:
              isHasArrow && edge.getModel().edgetype === 'two-way'
                ? {
                  path: G6.Arrow.triangle(5, 10, defaultRadius),
                  d: defaultRadius
                }
                : false,
            endArrow: isHasArrow
              ? {
                path: G6.Arrow.triangle(5, 10, defaultRadius),
                d: defaultRadius
              }
              : false
          }
        })
      })

      // this.animateFun(); // 搜索出来的节点动画
      // 保存所有节点的 id(搜索定位的功能使用该项)
      this.getAllNodeId()
      const zoom = this.graph.getZoom()
      // this.graph.get('canvas').set('localRefresh', false);
      //将graph对象发布给各子组件
      this.$eventBus.$emit('getGraph', this.graph)
      this.getConfigDetail()
    },

    getConfigDetail() {
      this.$axios
        .get(this.$api.setting.showConfigApi.getConfigDetail)
        .then((res) => {
          const { data, code } = res.data;
          if (code == 200) {
            // console.log(data, 'data');
            if (data.ontologyPropertyNode == '默认展开') {
              this.propertyExpend = false
            } else {
              this.propertyExpend = true
            }
            let nodeLabel = ""
            if (data.nodeLabel == '文字置底') {
              nodeLabel = "1"
            } else {
              nodeLabel = "2"
            }
            this.setProperty({
              isInitSet: true,
              initData: data
            })
            this.changeNodeTextSize(data.textSize)
            this.changeEdgeTextSize(data.sideSize)
            this.refreshGraphLayout(data.ontologyLayoutWay)
            this.changeGraphConfig({
              nodeShape: "2",
              nodeLabel: nodeLabel
            })
          }
        });
    },
    // graph实例用来生成缩略图
    initGraphForImage() {
      if (!this.subGraph) {
        // let subGraphContainer = document.getElementById('subGraphContainer');
        let { width, height } = this.getSubGraphSize()
        this.subGraph = new G6.Graph({
          container: 'subGraphContainer',
          width,
          height,
          renderer: 'canvas',
          fitCenter: true,
          linkCenter: true,
          // layout: {
          //     type: 'grid',
          //     linkDistance: 20,
          //     preventOverlap: true, // 防止节点覆盖
          // },
          defaultNode: {
            size: this.defaultNodeRadius * 2,
            style: {
              fill: '#7E9BFA', // 填充色
              stroke: '#BFC0C0', // 描边颜色
              lineWidth: 1, // 描边宽度
              cursor: 'pointer'
            },
            clipCfg: {
              show: true,
              type: 'circle',
              r: 30,
              style: {
                lineWidth: 1
              }
            }
          },
          defaultEdge: {
            type: 'line',
            style: {
              lineWidth: '2',
              stroke: '#A9B6C3',
              // lineAppendWidth: 10, // 如果两个节点间存在多条边儿, 选中边有 bug
              // startArrow: true,
              endArrow: {
                path: G6.Arrow.triangle(5, 10, this.defaultNodeRadius + 2),
                d: this.defaultNodeRadius + 2
              }
            },
            labelCfg: {
              autoRotate: true
            }
          },
          nodeStateStyles: {
            hover: {
              fill: 'lightsteelblue'
            },
            click: {
              stroke: '#086788',
              lineWidth: 2
            }
          },
          edgeStateStyles: {
            click: {
              stroke: '#FFB997'
            }
          }
        })
      }
      G6.Util.processParallelEdges(this.selectedItems.edges)
      this.subGraph.clear()
      let { width, height } = this.getSubGraphSize()
      this.subGraph.changeSize(width, height)
      this.subGraph.data(this.selectedItems)
      this.subGraph.render()
    },
    getSubGraphSize() {
      let selectedNodes = this.selectedItems.nodes
      let minX = 0
      let minY = 0
      let maxX = 0
      let maxY = 0
      let padding = 30
      selectedNodes.forEach(node => {
        minX = minX === 0 ? node.x : Math.min(minX, node.x)
        minY = minY === 0 ? node.y : Math.min(minY, node.y)
        maxX = Math.max(node.x, maxX)
        maxY = Math.max(node.y, maxY)
      })
      selectedNodes.forEach(node => {
        node.x = node.x - minX + padding
        node.y = node.y - minY + padding
      })
      let maxL = Math.max(maxX, maxY) + padding * 2
      return {
        width: maxL,
        height: maxL
      }
    },
    initGraphEvent() {
      this.graph.on('cancelCreatEdge', edgeItem => {
        this.clearAllStates()
        this.graph.setMode('default')
      })
      this.graph.on('wheelzoom', zoomVal => {
        if (zoomVal >= 9.15) {
        } else {
          this.zoomVal = zoomVal
        }
      })
      // '画布上'连线完成之后, 切换一下模式
      this.graph.on('aftercreateedge', edgeItem => {
        this.isShowNode = false
        // 先发开侧边栏, 添加节点信息, 之后保存, 使用返回的信息构建 model, 然后添加节点
        if (this.flag) {
          this.change_drawerFlag()
        }
        const singleEdge = deepClone(edgeItem.edge.getModel())
        let { id, source, target } = singleEdge
        let sourceNode = this.graph.findById(source).getModel()
        let targetNode = this.graph.findById(target).getModel()
        let sourceId = sourceNode.ontologyId
        let targetId = targetNode.ontologyId
        let sourceName = sourceNode.label
        let targetName = targetNode.label
        this.create_new_edge_of_g6({
          edgeId: id,
          sourceId,
          targetId,
          sourceName,
          targetName
        })
        this.$refs.editEdgeForm.createEdge().then(res => {
          if (!res) {
            // 创建失败时要删除新增线
            let delEdgeId = this.newEdgeOfG6.edgeId
            this.removeEdge({
              id: delEdgeId
            })
          }
          this.$refs.child.handleClick('detail')
          const edges = this.graph.save().edges
          G6.Util.processParallelEdges(edges)
          this.graph.getEdges().forEach((edge, i) => {
            this.graph.updateItem(edge, {
              curveOffset: edges[i].curveOffset,
              curvePosition: edges[i].curvePosition
            })
          })
          this.graph.paint()
        })

        this.clearAllStates()
        this.graph.setMode('default')
      })

      this.graph.on('node:click', async e => {
        this.isShowNode = true
        if (this.flag) {
          this.change_drawerFlag()
        }
        // 取消所有点/边被单击的状态
        this.claerItemClicked()
        // 获取当前点击的节点
        const nodeItem = e.item
        this.graph.setItemState(nodeItem, 'click', true)
        const singleNode = deepClone(e.item.getModel())
        this.change_current_node(singleNode)
        this.change_drawer_type_of_node('detail')
        if (this.flag) {
          this.change_drawerFlag()
        }
      })
      this.graph.on('node:dblclick', async e => {
        // 取消所有点/边被单击的状态
        this.claerItemClicked()
        const nodeItem = e.item
        this.graph.setItemState(nodeItem, 'click', true)
        const singleNode = e.item.getModel()
        let ontologyId = singleNode.ontologyId
        this.expandGraph(ontologyId, singleNode)
      })
      this.graph.on('edge:click', async e => {
        this.isShowNode = false
        if (this.flag) {
          this.change_drawerFlag()
        }
        // 取消所有点/边被单击的状态
        this.claerItemClicked()
        // 获取当前点击的线
        const edgeItem = e.item

        this.graph.setItemState(edgeItem, 'click', true)
        const singleEdge = deepClone(e.item.getModel())
        this.change_current_edge(singleEdge)
        this.change_drawer_type_of_edge('detail')
      })
      // 当 click-select 选中的元素集合发生变化时将会触发下面时机事件，e 中包含相关信息
      this.graph.on('nodeselectchange', e => {
        let selectNodes = e.selectedItems.nodes
        let selectEdges = e.selectedItems.edges
        this.selectedItems.nodes = selectNodes.map(node => {
          let { id, ontologyId, label, x, y } = node.getModel()
          return {
            id,
            ontologyId,
            label,
            x,
            y
          }
        })
        this.selectedItems.edges = selectEdges.map(edge => {
          let { id, source, target, label, edgeType } = edge.getModel()
          return {
            id,
            source,
            target,
            label,
            edgeType
          }
        })
        // 当前是否有选中内容
        this.isSelectGroup = selectNodes.length || selectEdges.length
      })
    },
    getALLGrapgData() {
      const { edges, nodes } = this.graph.save()
      const nodesIds = []
      const edgesIds = []
      nodes.forEach(item => {
        nodesIds.push(item.id)
      })
      edges.forEach(item => {
        edgesIds.push(item.id)
      })
      return {
        nodes,
        edges,
        nodesIds,
        edgesIds
      }
    },
    // 展开节点接口
    async expandGraph(ontologyId, all) {
      let oldEntityIdList = []

      this.graphDatas.ontologyNodes.forEach(item => {
        oldEntityIdList.push(item.ontologyId)
      })
      let res = await this.$axios.post(
        this.$api.ontologyModel.xuekuiObj.expandGraph,
        {
          ontologyId: ontologyId,
          oldEntityIdList: oldEntityIdList
        }
      )
      const { data, code, message } = res.data
      if (code === 200) {
        const { Nodes, Edges } = data
        const { nodesIds, edgesIds } = this.getALLGrapgData()

        const { x: oldX, y: oldY } = all
        // 解决G6 id不是string类型的报错
        Nodes.forEach(item => {
          if (nodesIds.indexOf(`${item.id}`) == -1) {
            item.id = item.id + ''
            item.x = oldX
            item.y = oldY
            item.color = item.color
            item.style = {
              fill: item.color,
              stroke: '#BFC0C0'
            }
            item.size = 30
            item.ontologyId = item.ontologyId
            item.label = item.label
            let addNode = this.graph.addItem('node', item, false)
            addNode && this.graph.setItemState(addNode, 'default', true)
          }
        })
        Edges.forEach(item => {
          if (edgesIds.indexOf(`${item.id}`) == -1) {
            item.source = item.startNodeId
            item.target = item.endNodeId
            item.id = item.id + ''
            item.color = item.color || '#8D99AE'
            // item.color = item.color
            this.graph.addItem('edge', item)
          }
        })
        this.graph.paint()
        G6.Util.processParallelEdges(Edges)
        const subLayout = new G6.Layout.circular({
          center: [oldX, oldY],
          linkDistance: 300,
          preventOverlap: true,
          // animate: true,
          tick: () => {
            this.graph.refreshPositions()
          }
        })
        let nodes = Nodes.map(node => {
          node.id = '' + node.id
          return node
        })
        let edges = Edges.map(edge => {
          edge.id = '' + edge.id
          return edge
        })
        subLayout.init({
          nodes,
          edges
        })
        subLayout.execute()
        this.graph.positionsAnimate()
        this.handleArrow()
      }
    },

    handleArrow() {
      G6.Util.processParallelEdges(this.graph.save().edges)
      this.graph.getEdges().forEach(edge => {
        let isHasArrow = edge.getModel().type === 'loop' ? false : true
        let distance = this.defaultNodeRadius + 2
        this.graph.updateItem(edge, {
          style: {
            endArrow: isHasArrow
              ? {
                path: G6.Arrow.triangle(5, 10, distance),
                d: distance
              }
              : false
          }
        })
      })
    },

    // 切换选择组件状态
    selectGroup() {
      this.dialogTableVisible = true
    },
    // 模态框关闭后
    afterDialogClose() {
      this.$refs.dialogForm.resetFields()
    },
    // 取消
    resetDialogForm() {
      this.dialogFormVisible = false
    },
    // 保存组件
    submitDialogForm() {
      this.$refs.dialogForm.validate(valid => {
        if (valid) {
          if (this.subGraph) {
            this.loading = true
            const dataURL = this.subGraph.toDataURL('image/jpeg', '#fff')
            this.ontologyElementImgUrl = dataURL
            let nodes = this.selectedItems.nodes.map(node => {
              let { id, x, y } = node
              let item = {
                nodeId: id,
                x,
                y
              }
              return item
            })
            let edgeIds = this.selectedItems.edges.map(edge => {
              return edge.id
            })
            let params = {
              ...this.elementData,
              nodes,
              edgeIds,
              ontologyElementImgUrl: this.ontologyElementImgUrl
            }
            this.$axios
              .post(this.$api.ontologyModel.xuekuiObj.saveElement, params)
              .then(res => {
                let code = res.data.code
                let message = res.data.message
                if (+code !== 200) {
                  this.$message.warning(message)
                  this.loading = false
                  return
                }
                this.loading = false
                this.dialogFormVisible = false
                this.$message.success(
                  '组件保存成功，可在组件管理模块查看已保存的组件'
                )
              })
              .catch(err => {
                this.loading = false
                console.error(err)
              })
          }
        } else {
          return false
        }
      })
    },
    // 关闭组件列表模态框
    resetDialogTable() {
      this.dialogTableVisible = false
    },
    downloadGraph(type) {
      if (this.graph) {
        this.graph.downloadFullImage('知识图谱', type, {
          backgroundColor: '#ddd',
          padding: [30, 15, 15, 15]
        })
      } else {
        this.$notify({
          title: '提示',
          message: '请先生成图谱！',
          type: 'warning',
          duration: 1500
        })
      }
    },
    // 下载svg图片
    downloadSvgGraph() {
      this.graph.toFullDataURL(
        res => {
          // 获取导出图片的高宽信息
          const imgTmp = new Image()
          imgTmp.src = res
          imgTmp.onload = () => {
            const svgData = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${imgTmp.width}" height="${imgTmp.height}" style="background-color: #ddd;"><defs></defs><g><image width="${imgTmp.width}" height="${imgTmp.height}" preserveAspectRatio="none" xlink:href="${res}"></image></g></svg>`
            const blobData = new Blob([svgData], {
              type: 'image/svg+xml;charset=utf-8'
            })
            const svgUrl = URL.createObjectURL(blobData)
            const link = document.createElement('a')
            link.href = svgUrl
            link.download = '知识图谱.svg'
            document.body.appendChild(link)
            link.click()
            document.body.removeChild(link)
            URL.revokeObjectURL(svgUrl)
          }
        },
        'image/png',
        {
          backgroundColor: '#ddd',
          padding: [30, 15, 15, 15]
        }
      )
    },
    // 导入组件
    submitDialogTable(ids) {
      this.dialogTableVisible = false
      let { nodes, edges } = ids
      nodes.forEach(node => {
        node = {
          ...node,
          size: this.defaultNodeRadius * 2,
          style: {
            fill: '#7E9BFA'
          }
        }
        this.graph.addItem('node', node, false)
      })
      edges.forEach(edge => {
        this.addEdge(edge)
      })
      const _edges = this.graph.save().edges
      G6.Util.processParallelEdges(_edges)
      this.graph.setMode('default')
      this.getGraphData(this.ontologyAllId)
        .then(res => {
          this.create_node_config_data(deepClone(res.ontologyNodes))
          this.create_edge_config_data(deepClone(res.ontologyEdges))
        })
        .catch()
    },

    // // 图片转 base64
    // convertImgToBase64(url, cb, ouputFormat) {
    //   const img = new Image();
    //   let canvas = document.createElement("canvas");
    //   const ctx = canvas.getContext("2d");
    //   let dataURL;
    //   img.crossOrigin = "Anonymous";
    //   img.onload = function () {
    //     canvas.width = img.width;
    //     canvas.height = img.height;
    //     ctx.drawImage(img, 0, 0);
    //     dataURL = canvas.toDataURL(ouputFormat || "image/png");
    //     cb.call(this, dataURL);
    //     canvas = null;
    //   };
    //   img.src = url;
    //   return dataURL;
    // },

    main(src, cb) {
      var image = new Image()
      image.src = src + '?v=' + Math.random() // 处理缓存
      image.crossOrigin = '*' // 支持跨域图片
      image.onload = function () {
        var base64 = getBase64Image(image)
        cb && cb(base64)
      }
    },

    changeGraphConfig({ nodeShape, nodeLabel }) {
      if (!this.graph) return
      const nodes = this.graph.getNodes()
      // 对节点形状的处理
      switch (nodeShape) {
        case '1':
          nodes.forEach(item => {
            if (item._cfg.model.img) {
              item._cfg.model.img =
                'https://gw.alipayobjects.com/os/s/prod/antv/assets/image/logo-with-text-73b8a.svg'

              item._cfg.type = 'image'
              // item._cfg.keyShape = "ImageShape";
              item._cfg.attrs = {
                src:
                  'https://gw.alipayobjects.com/os/s/prod/antv/assets/image/logo-with-text-73b8a.svg'
              }
              this.graph.updateItem(item, {
                type: 'image',
                clipCfg: {
                  show: true
                }
              })
            }
          })
          break
        case '2':
          nodes.forEach(item => {
            if (item._cfg.model.img) {
              this.graph.updateItem(item, {
                type: 'circle',
                clipCfg: {
                  show: false
                }
              })
            }
          })
          break
      }
      // 对节点标签的处理
      switch (nodeLabel) {
        case '1':
          nodes.forEach(item => {
            this.graph.updateItem(item, {
              labelCfg: {
                position: 'bottom' //"center"为内嵌，"bottom"为置底
              }
            })
          })
          break
        case '2':
          nodes.forEach(item => {
            this.graph.updateItem(item, {
              labelCfg: {
                position: 'center' //"center"为内嵌，"bottom"为置底
              }
            })
          })
          break
      }
    },
    updateNodeColor(params) {
      return this.$axios
        .post(
          this.$api.ontologyModel.xuekuiObj.configurationOntologyColor,
          params
        )
        .then(res => {
          let code = res.data.code
          if (code !== 200) {
            let message = res.data.message
            this.$message.warning(message)
            return false
          }
          return true
        })
    },
    updateEdgeColor(params) {
      return this.$axios
        .post(this.$api.ontologyModel.xuekuiObj.configurationSpoColor, params)
        .then(res => {
          let code = res.data.code
          if (code !== 200) {
            let message = res.data.message
            this.$message.warning(message)
            return false
          }
          return true
        })
    },
    saveConfig(eleLoading) {
      let nodes = this.node_config_data.map(node => {
        let item = {
          id: node.id,
          color: node.color
        }
        return item
      })

      let edges = this.edge_config_data.map(edge => {
        let item = {
          id: edge.id,
          color: edge.color
        }
        return item
      })
      nodes.shift()
      edges.shift()
      let nodeParams = {
        ontologyAllId: this.ontologyAllId,
        param: nodes,
        propertyColor: this.propertyColor,
        propertyStyle: this.propertyStyle
      }
      let edgeParams = {
        ontologyAllId: this.ontologyAllId,
        param: edges
      }
      return Promise.all([
        this.updateNodeColor(nodeParams),
        this.updateEdgeColor(edgeParams)
      ])
        .then(res => {
          eleLoading.close()
          if (res.every(() => true)) {
            this.$message.success('配置成功!')
          }
        })
        .catch(err => {
          console.error(err)
          eleLoading.close()
        })
    },
    // 同步面板与画布中节点配置的函数
    changeNodeColorFun() {
      let configDataNodes = deepClone(this.node_config_data)
      let configDataEdges = deepClone(this.edge_config_data)
      configDataNodes.shift()
      this.graph.getNodes().forEach(item => {
        configDataNodes.forEach(data => {
          if (data.id === item.getModel().id) {
            let model = {
              size: data.radius * 2,
              style: {
                fill: data.color
              }
            }
            this.graph.updateItem(item, model)
            let edges = item.getEdges()
            edges.forEach(edge => {
              let edgeConfig = edge.getModel()
              let configDataEdge = configDataEdges.find(
                edge => edge.id === edgeConfig.id
              )
              this.updateArrowSize(edge, configDataEdge)
            })
          }
        })
      })
    },

    // 同步面板与画布中连线配置的函数
    changeEdgeColorFun() {
      let configData = deepClone(this.edge_config_data)
      configData.shift()
      this.graph.getEdges().forEach(item => {
        configData.forEach(data => {
          let modelConfig = item.getModel()
          if (data.id === modelConfig.id) {
            item.setState('click', false)
            this.updateArrowSize(item, data)
          }
        })
      })
    },
    updateArrowSize(item, data) {
      let edgeConfig = item.getModel()
      let isHasArrow = edgeConfig.type === 'loop' ? false : true
      let isTwoWay = edgeConfig.edgetype === 'two-way' ? true : false
      let isArrow = data ? data.isArrow : true
      let stroke = data && data.color ? data.color : '#A9B6C3'
      let lineWidth = data && data.edgeWidth ? data.edgeWidth : '2'
      let sourceNodeSize = this.graph.findById(edgeConfig.source).getModel()
        .size
      let targetNodeSize = this.graph.findById(edgeConfig.target).getModel()
        .size

      let sourceNodeRadius = sourceNodeSize
        ? sourceNodeSize / 2 + 2
        : this.defaultNodeRadius + 2
      let targetNodeRadius = targetNodeSize
        ? targetNodeSize / 2 + 2
        : this.defaultNodeRadius + 2
      let model = {
        style: {
          stroke,
          lineWidth,
          endArrow:
            isArrow && isHasArrow
              ? {
                path: G6.Arrow.triangle(5, 10, targetNodeRadius),
                d: targetNodeRadius
              }
              : false,
          startArrow:
            isArrow && isHasArrow && isTwoWay
              ? {
                path: G6.Arrow.triangle(5, 10, sourceNodeRadius),
                d: sourceNodeRadius
              }
              : false
        }
      }
      this.graph.updateItem(item, model)
    },
    async undo() {
      if (this.undoList.length == 0) {
        this.$message.warning('没有操作可撤销了！')
        return
      }
      let params = {
        operationId: this.undoList[this.undoList.length - 1].operationId
      }
      let res = await this.$axios.get(this.$api.ontologyModel.xuekuiObj.undo, {
        params
      })
      let type = this.undoList[this.undoList.length - 1].type
      if (res.data.code === 200) {
        this.undoList.pop()
        if (type == 1) {
          // 新增
          // 刷新画布
          this.removeEdge({
            id: res.data.data
          })
        } else {
          // 修改
          this.changeByNode(res.data.data)
          // this.changeByEdge(res.data.data, "edit");
        }
      }
    },
    async clearGraph() {
      let res = await this.$axios.get(
        this.$api.ontologyModel.xuekuiObj.clearGraph,
        {
          params: { ontologyAllId: this.ontologyAllId }
        }
      )
      if (res.data.code === 200) {
        this.$message({
          type: 'success',
          message: '删除成功!'
        })
        this.graph.clear()
      } else {
        this.$message.error(res.data.message)
      }
    }
  },

  watch: {
    // zoomVal(newVal, oldVal) {
    //   this.graph.zoomTo(newVal);
    // },
    node_config_data: {
      handler: function (newVal, oldVal) {
        this.changeNodeColorFun()
      },
      deep: true
    },
    edge_config_data: {
      handler: function (newVal, oldVal) {
        this.changeEdgeColorFun()
      },
      deep: true
    }
  }
}
</script>
<style lang="scss" scoped>
.graphVis {
  width: 100%;
  height: 100%;
  top: -24px;
  position: relative;

  #graphContainer {
    width: 100%;
    height: 100%;

    /deep/ canvas {
      left: 0;
      bottom: 0;
      width: 100% !important;
      height: 100% !important;
      z-index: -1 !important;
      background: #f8f8f8;
      cursor: pointer;
    }

    /deep/ canvas:-webkit-full-screen {
      width: 100%;
      height: 100%;
    }

    /deep/ .g6-minimap {
      position: absolute !important;
      right: 60px !important;
      bottom: 0px !important;
      overflow: visible !important;
    }

    /deep/.g6-minimap-container {
      border: 1px solid #e2e2e2;
      overflow: hidden;
    }

    // /deep/ .g6-minimap-viewport {
    //   // width: 150px !important;
    //   // // height: 98px;
    //   // left: auto;
    //   // top: auto;
    //   // margin-left: 2px;
    //   // margin-top: 2px;
    // }

    /deep/ .g6-component-contextmenu {
      position: absolute;
      z-index: 2;
      list-style-type: none;
      background-color: #f8f8f8;
      border-radius: 6px;
      font-size: 14px;
      color: rgba(88, 83, 83, 0.6);
      width: fit-content;
      transition: opacity 0.2s;
      text-align: center;
      box-shadow: 0 5px 18px 0 rgba(200, 200, 200, 0.9);
      border: 0px;
    }

    /deep/ .g6-component-contextmenu ul {
      padding-left: 0px;
      margin: 0;
    }

    /deep/ .g6-component-contextmenu li {
      padding: 0px 20px 0px 20px;
      cursor: pointer;
      list-style-type: none;
      list-style: none;
      margin-left: 0;
      line-height: 38px;
    }

    /deep/ .g6-component-contextmenu li:hover {
      color: #363b40;
    }

    // /deep/.toolbar.hideRight {
    //   right: 500px;
    // }

    /deep/ .toolbar {
      position: absolute;
      right: 50px;
      top: 240px;
      color: #545454;
      cursor: pointer;
      width: 48px;
      padding: 0;
      margin: 0;

      li {
        position: relative;
        width: 48px;
        height: 48px;
        background: #ffffff;
        margin-bottom: 4px;
        font-size: 12px;
        border-radius: 4px;
        cursor: pointer;
        transition: 1s;

        &:hover {
          background-color: wheat;
        }

        .toolbar-main {
          position: relative;
          width: 100%;
          height: 100%;
          display: flex;
          flex-direction: column;
          justify-content: center;
          align-items: center;

          &:hover {
            &+.toolbar-tips {
              z-index: 0;
              opacity: 1;
            }

            &+.toolbar-opera {
              z-index: 0;
              opacity: 1;
            }
          }
        }

        .toolbar-opera {
          position: absolute;
          left: -150px;
          top: -4px;
          opacity: 0;
          transition: 1s;
          z-index: -1;

          &:hover {
            z-index: 0;
            opacity: 1;
          }

          >span {
            display: block;
            box-shadow: 1px 1px 4px;
            background: white;
            padding: 5px;
            color: black;
            border-radius: 6px;
            width: 130px;
            text-align: center;

            &:not(:last-child) {
              margin-bottom: 10px;
            }
          }
        }

        .toolbar-tips {
          opacity: 0;
          box-shadow: 1px 1px 4px;
          position: absolute;
          background: white;
          padding: 5px;
          color: black;
          border-radius: 6px;
          width: 130px;
          text-align: center;
          transition: 1s;
          z-index: -1;

          &:hover {
            z-index: 0;
            opacity: 1;
          }
        }

        // .icon {
        //   position: relative;

        //   span {
        //     position: absolute;
        //     left: 16px;
        //     top: 4px;
        //   }
        // }

        // .content {
        //   position: relative;
        //   top: 20px;
        //   text-align: center;
        // }
      }
    }
  }

  .layoutComp {
    position: absolute;
    right: 50px;
    top: 20px;
    transition: right 0.7s ease 0s;
    z-index: 9;
  }

  .hideRight.layoutComp {
    right: 474px;
  }

  .sliderComp {
    position: absolute;
    right: 50px;
    top: 500px;
    transition: right 0.7s ease 0s;
    z-index: 9;

    /deep/ .el-slider {
      height: 150px;
    }
  }

  .hideRight.sliderComp {
    right: 474px;
  }

  /deep/ .el-slider__button-wrapper {
    width: 30px;
    height: 30px;
    left: -12px !important;
  }

  /deep/ .el-slider__button {
    width: 10px;
    height: 10px;
  }

  .addWrapper {
    width: 330px;
    height: 40px;
    background: #ffffff;
    box-shadow: 0px 4px 4px 4px rgba(172, 176, 191, 0.14);
    border-radius: 2px;
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    right: 400px;
    top: 20px;
    transition: right 0.7s ease 0s;
    box-sizing: border-box;
    z-index: 9;

    .addLeft,
    .addRight {
      position: relative;
      flex: 1;
      padding: 10px 0;
      height: 20px;
      display: flex;
      justify-content: space-evenly;
      align-items: center;

      &:hover {
        // background-color: pink;
        cursor: pointer;
      }
    }

    .addRight {
      .addNodeComp {
        border-left: 1px solid #f2f2f2;
      }
    }

    .addNodeComp {
      position: relative;
      flex: 1;
      width: 80%;
      height: 20px;
      display: flex;
      justify-content: space-evenly;
      align-items: center;

      div {
        margin-right: 0;
      }

      span {
        display: flex;
        align-items: center;
      }
    }
  }

  .hideRight.addWrapper {
    right: 824px;
  }

  // .addNodeComp {
  //   width: 100px;
  //   height: 40px;
  //   display: flex;
  //   justify-content: space-evenly;
  //   align-items: center;
  //   position: absolute;
  //   right: 400px;
  //   top: 0;
  //   border: 1px solid #999999;
  //   border-radius: 4px;
  //   background-color: #fff;
  //   transition: right 0.7s ease 0s;

  //   &:hover {
  //     border-color: #3a58b9;
  //     cursor: pointer;
  //   }

  //   span {
  //     display: flex;
  //     align-items: center;
  //   }

  //   &.center {
  //     top: 50px;
  //   }

  //   &.left {
  //     top: 100px;
  //   }
  // }

  // .hideRight.addNodeComp {
  //   right: 824px;
  // }

  #subGraphContainer {
    position: absolute;
    top: 0;
    left: 0;
    width: 400px;
    height: 400px;
    z-index: -10;
  }

  .subGraphImg {
    position: absolute;
    width: 200px;
    height: 200px;
    bottom: 0;
    left: 0;
  }

  .dialogForm {
    /deep/ .el-dialog {
      width: 500px;
      border-radius: 5px;
    }

    .dialog-inline {
      /deep/ .el-form-item__content {
        width: 300px;
      }

      button.el-button {
        background-color: $--color-primary !important;
        color: #fff !important;
        line-height: 1.2;
      }
    }
  }

  .el-tab-pane>div {
    height: 100%;
  }
}
</style>
